<script lang="ts">
import Vue from "vue";
import { Component, Model, Prop } from "vue-property-decorator";
import { v4 as uuidv4 } from "uuid";
import { StateNode, Branch, DragStart, GroupNode } from "./dto";
import nodes from "./node/index.vue";
import rectComponent from "./rect/index.vue";
import lineComponent from "./line/index.vue";

@Component({ components: { nodes, rectComponent, lineComponent } })
class Main extends Vue {
  @Model("change") data: Array<StateNode>;

  @Prop(Array) nodes: Array<GroupNode>;

  /**
   * 当前线的开始坐标
   * @see 避免使用width，height变量来计算，使用运行时动态获取宽高。
   * @see 以免ui字体，padding等改动，导致老数据的width，height计算出问题。
   */
  get currentPosition() {
    return (node: StateNode, branch: Branch) => {
      let dom = document.getElementById(node.stateId);
      if (!dom) return null;
      let x = dom.offsetLeft + dom.offsetWidth / 2 + node.x;
      let y = dom.offsetTop + dom.offsetHeight + node.y;
      return [x, y];
    };
  }

  /**
   * 下一个状态，连线终点坐标
   * @see 同currentPosition
   */
  get nextPosition() {
    return nextId => {
      let dom = document.getElementById(nextId);
      if (!dom) return null;
      let data = this.data.find(v => v.stateId == nextId);
      return data ? [data.x + dom.offsetWidth / 2, data.y] : [];
    };
  }

  /**
   * 处理拖拽
   * @see 暂时只支持开始节点，结束节点，自定义节点。
   */
  drop(e: any) {
    let data: DragStart = JSON.parse(e.dataTransfer.getData("node"));
    if (data.nodeType == "reader" && this.data.find(v => v.nodeType == "reader")) {
      return false;
    }
    if (data.nodeType == "writer" && this.data.find(v => v.nodeType == "writer")) {
      return false;
    }
    if (data.nodeType == "filter" && this.data.find(v => v.nodeType == "filter")) {
      return false;
    }
    if (data.type == "start" && this.data.find(v => v.type == "start")) {
      return false;
    }
    let obj = {
      ...data,
      stateId: uuidv4(),
      x: e.offsetX - data.x,
      y: e.offsetY - data.y,
      branches: []
    };
    this.data.push(obj);
    this.showBranch(obj);
  }

  /**
   * 删除状态
   * @see 引用子组件的resetPostion方法，如果不调用，会出现子组件变更后原始坐标丢失，拖拽移动会出现不聚集BUG。
   * @see 删除本身，并且和本身相关联的分支
   * @see 第一次$nextTick在于svg本身不支持自适应宽高问题，需手动forceUpdate计算。
   * @see 第二次$nextTick由于多个引用分支变化，需要重设坐标。
   */
  deleteFlow(data: StateNode, index: number) {
    this.data.map((v, i) => {
      (v.branches || []).map((vv, ii) => {
        if (vv.nextStateId == data.stateId) {
          this.data[i].branches[ii].nextStateId = "";
          this.data[i].branches.splice(ii, 1); //删除指向已经被删除节点的标签
        }
      });
    });
    this.$emit('delete-data-factory', this.data[index], index)
    this.data.splice(index, 1);
    this.reRender();
  }

  reRender(cb?: Function) {
    this.$nextTick(() => {
      this.$forceUpdate();
      cb && cb();
    });
  }

  /**
   * 节点分支
   */
  showBranch(data: StateNode) {
    this.$emit("click", data);
  }

  /**
   * 编辑
   */
  editFlow(index: number) {
    this.$emit("edit", this.data[index]);
  }
  /**
   * 编辑任务
   */
  editTask(index: number) {
    this.$emit("edit-task", this.data[index]);
  }
  editDataFactory(index: number) {
    this.$emit("edit-data-factory", this.data[index]);
  }
  mounted() {
    this.reRender();
  }
}
export default Main;
</script>

<template>
  <div class="flow-skill-state-container-v1">
    <div class="node-container">
      <div class="node-states"><nodes :nodes="nodes" /></div>
      <!-- 流程图 -->
      <div id="work-space" class="work-space">
        <svg id="svg" @drop="drop" @dragover.prevent>
          <g id="svg-group">
            <g v-for="(item, index) in data" :key="index">
              <!-- 流程节点 -->
              <rect-component
                v-model="data[index]"
                :stateId="stateId"
                @click="showBranch(item)"
                @edit="editFlow(index)"
                @edit-task="editTask(index)"
                @edit-data-factory="editDataFactory(index)"
                @delete="() => deleteFlow(item, index)"
              />
              <line-component
                v-for="(v, i) in item.branches"
                :key="i"
                :from="currentPosition(item, v)"
                :to="nextPosition(v.nextStateId)"
              />
            </g>
          </g>
        </svg>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
@import "./index.scss";
</style>
